<?php

class Email {

	private $notfound;
	
	private $uid;
	private $msgno;
	private $date;
	private $subject;
	private $from;
	private $to;
	private $recent;
	private $flagged;
	private $answered;
	private $deleted;
	private $seen;
	private $draft;
	
	private $htmlmsg;
	private $plainmsg;
	private $charset;
	private $attachments;
	
	private $cc;
	private $bcc;
	private $fromSimple;
	private $toSimple;
	private $ccSimple;
	private $bccSimple;
	
	
	public function __get($var) {
		return $this->$var;
	}
	
	public function __set($var, $value) {
		$this->$var = $value;
	}
	
	public function __construct($val) {
		$this->uid = $val->uid;
		$this->msgno = isset($val->msgno) ? $val->msgno : '';
		$this->date = isset($val->date) ? $val->date : '';
		if(empty($val->subject))
			$this->subject = "Aucun objet";
		else
			$this->subject = Email::header_charset_decode($val->subject);
		$this->from = Email::header_charset_decode($val->from);
		if(empty($this->from)) $this->from = "Expéditeur inconnu";
		$this->to = (!empty($val->to) ? $val->to : "Destinataire inconnu");
		$this->recent = isset($val->msgno) ? $val->recent : '';
		$this->flagged = isset($val->flagged) ? $val->flagged : '';
		$this->answered = isset($val->answered) ? $val->answered : '';
		$this->deleted = isset($val->deleted) ? $val->deleted : '';
		$this->seen = isset($val->seen) ? $val->seen : '';
		$this->draft = isset($val->draft) ? $val->draft : '';
		
		$this->cc = isset($val->cc) ? $val->cc : '';
		$this->bcc = isset($val->bcc) ? $val->bcc : '';
		
		$this->attachments = array();
	}
	
	static public function header_charset_decode($text) {
		$text = imap_mime_header_decode($text);
		$return = '';
		foreach($text as $s) {
			$return .= (strtolower($s->charset) == "utf-8") ? $s->text : mb_convert_encoding($s->text,'UTF-8');
		}
		return $return;
	}
	
	static public function findInRange($page,$mailbox_name = INBOX) {
		$emails = array();
		$mailbox = Imap::open($mailbox_name);
		$message_count = imap_num_msg($mailbox);
		// Calcul du range
		$min = $message_count-$page * NB_MAX + 1;
		if($min < 1) $min = 1;
		if($min > $message_count) $min = -1;
		$max = $message_count - ($page - 1) * NB_MAX;
		if($max > $message_count) $max = $message_count;
		if($max < 1) $max = -1;
		if($min != -1 && $max != -1) {
			$range = $min . ':' . $max;
			// Récupération des messages
			$overview = imap_fetch_overview($mailbox, $range);
			krsort($overview);
			foreach ($overview as $val) {
				// Création de l'objet Email
				$email = new Email($val);
				$emails[] = $email;
			}
		}
		Imap::close($mailbox);
		return $emails;
	}
	
	public static function findInRangeFromTo($pagefrom,$pageto,$mailbox_name = INBOX) {
		$emails = array();
		$mailbox = Imap::open($mailbox_name);
		$message_count = imap_num_msg($mailbox);
		// Calcul du range
		$min = $message_count-$pageto * NB_MAX + 1;
		if($min < 1) $min = 1;
		if($min > $message_count) $min = -1;
		$max = $message_count - ($pagefrom - 1) * NB_MAX;
		if($max > $message_count) $max = $message_count;
		if($max < 1) $max = -1;
		if($min != -1 && $max != -1) {
			$range = $min . ':' . $max;
			// Récupération des messages
			$overview = imap_fetch_overview($mailbox, $range);
			krsort($overview);
			foreach ($overview as $val) {
				// Création de l'objet Email
				$email = new Email($val);
				$emails[] = $email;
			}
		}
		Imap::close($mailbox);
		return $emails;
	}
	
	static public function findEmailByUid($mailbox_name,$uid){
		$mailbox = Imap::open($mailbox_name);
		// Récupération des headers
		$overview = imap_headerinfo ( $mailbox, imap_msgno($mailbox,$uid) );
		$notfound = ($overview == NULL) ? true : false;
		$oldto = isset($overview->to) ? $overview->to : '';
		$oldfrom = isset($overview->from) ? $overview->from : '';
		$oldcc = isset($overview->cc) ? $overview->cc : '';
		$oldbcc = isset($overview->bcc) ? $overview->bcc : '';
		$overview->to = Email::stringFromEmailArray ( $oldto ); 
		$overview->from = Email::stringFromEmailArray ( $oldfrom );
		$overview->cc = Email::stringFromEmailArray ( $oldcc );
		$overview->bcc = Email::stringFromEmailArray ( $oldbcc );
		$overview->uid = $uid;
		$email = new Email($overview);
		$email->notfound = $notfound;
		// Récupération du contenu du mail text & html
		$email->getMsg($mailbox,$uid);
		$email->fromSimple = Email::stringSimpleFromEmailArray ( $oldfrom );
		$email->toSimple = Email::stringSimpleFromEmailArray ( $oldto );
		$email->ccSimple = Email::stringSimpleFromEmailArray ( $oldcc );
		$email->bccSimple = Email::stringSimpleFromEmailArray ( $oldbcc );
		Imap::close($mailbox);
		return $email;
	}
	
	public static function findUnseen($mailbox) {
		$emails = array();
		$mailbox = Imap::open($mailbox);
		$message_count = imap_num_msg($mailbox);
		$nos = imap_search($mailbox,'UNSEEN');
		if($nos != NULL) {
			$range = implode(',',$nos);
			$overview = imap_fetch_overview($mailbox, $range);
			krsort($overview);
			foreach ($overview as $val) {
				// Création de l'objet Email
				$email = new Email($val);
				$emails[] = $email;
			}
		}
		Imap::close($mailbox);
		return $emails;
	}
	
	static public function stringSimpleFromEmailArray ($var){
		// bon je pense qu'avec ça yaura pas de warning sur le foreach
		if ( !isset($var) || empty($var) || !is_array($var) || count($var)==0 ){
			return '';
		}
		$res = '';
		foreach ( $var as $el ){
			if ( !empty ($el->mailbox) && !empty ($el->host) ){
				$res .= ', ';
				$res .= Email::header_charset_decode($el->mailbox) . '@' . Email::header_charset_decode($el->host);
			}
		}
		$res = substr ( $res, 2 );
		return $res;
	}
	
	static public function completeEmailsFromStringSimple ($var){
		if ( empty($var) )
			return $var;
		$a = explode ( ',', $var );
		$b = array ();
		foreach ( $a as $v )
		{
			$v = trim ( $v );
			if ( ! preg_match ( "/@/", $v ) )
			{
				$v .= '@' . EMAILDOMAIN;
			}
			$b[] = $v;
		}
		$res = implode ( ',', $b );
		return $res;
	}
	
	static public function stringFromEmailArray ($var){
		if ( empty($var) ){
			return '';
		}
		$res = '';
		foreach ( $var as $el ){
			if ( !empty ($el->mailbox) && !empty ($el->host) ){
				$res .= ', ';
				if ( !empty ($el->personal) ) {
					$res .= '"' . Email::header_charset_decode($el->personal) . '" ';
				}
				$res .= '<' . Email::header_charset_decode($el->mailbox) . '@' . Email::header_charset_decode($el->host) . '>';
			}
		}
		$res = substr ( $res, 2 );
		return $res;
	}
	
	static public function formatContentForReply ( $text ){
		$res = '';
		foreach(preg_split("/(\r?\n)/", $text) as $line){
			$res .= '> ' . $line . "\r\n";
		}
		return $res;
	}
	
	static public function search($mailbox_name,$query){
		$mailbox = Imap::open($mailbox_name);
		$emails = array();
		$res = imap_search ( $mailbox, $query, SE_UID );
		$range = implode(',',$res);
		$overview = imap_fetch_overview($mailbox, $range, FT_UID);
		krsort($overview);
		//var_dump ( $overview );
		foreach ($overview as $val) {
			// Création de l'objet Email
			$email = new Email($val);
			$emails[] = $email;
		}
		Imap::close($mailbox);
		return $emails;
	}
	
	static public function makeLinksFromEmails($text){
		//traite les liens commençant par http et similaire
		$text = preg_replace (
			"#(((https?|s?ftp)://)([_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-/:?=&_@\#%;~\{\}\|\^\[\]\'\$\+\*]+)*))#",
			"<a href='$1' target='_blank'>$1</a>",$text );
		//traite les liens commençant par www
		$text = preg_replace (
			"#((^www\.)([_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-/:?=&_@\#%;~\{\}\|\^\[\]\'\$\+\*]+)*))#",
			"<a href='http://$1' target='_blank'>$1</a>",$text );
		//traite les adresses mails
		$text = preg_replace ( 
			"/([_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4}))/i",
			"<a href='" . WEB_ROOT . "compose/index/$1'>$1</a>",
			$text );
		return $text;
	}
	
	private function getMsg($connection,$uid) {
	    $structure = imap_fetchstructure($connection,$uid,FT_UID);

	    if(!$structure->parts) {
	        $this->getPart($connection,$uid,$structure,0);
	    } else {
	        foreach ($structure->parts as $partno0 => $part) {
	            $this->getPart($connection,$uid,$part,$partno0+1);
	        }
	    }
		
		$this->plainmsg =
			(
				(
					strtolower($this->charset) == "utf-8"
					||
					strtolower($this->charset) == "ascii"
				)
				? $this->plainmsg
				: mb_convert_encoding($this->plainmsg,'UTF-8')
			);
		$this->htmlmsg =
			(
				(
					strtolower($this->charset) == "utf-8"
					||
					strtolower($this->charset) == "ascii"
				)
				? $this->htmlmsg
				: mb_convert_encoding($this->htmlmsg,'UTF-8')
			);
	}

	private function getPart($connection,$uid,$p,$partno) {
	    $data = 
			(
				($partno)
				? imap_fetchbody($connection,$uid,$partno, FT_UID)
	        	: imap_body($connection,$uid, FT_UID)
			);

		if($p->encoding == 3) {
			$data = imap_base64($data);
		}
		if($p->encoding == 4) {
			$data = imap_qprint($data);
		}
		// no need to decode 7-bit, 8-bit, or binary

	    // PARAMETERS
	    $params = array();
	    if (isset($p->parameters)) {
	        foreach ($p->parameters as $x) {
	            $params[ strtolower( $x->attribute ) ] = $x->value;
			}
		}
	    if (isset($p->dparameters)) {
	        foreach ($p->dparameters as $x) {
	            $params[ strtolower( $x->attribute ) ] = $x->value;
			}
		}

	    
	    if (isset($params['filename']) || isset($params['name'])) { // ATTACHMENT
	        // filename may be given as 'Filename' or 'Name' or both
	        $filename = ($params['filename']) ? $params['filename'] : $params['name'];
	        // filename may be encoded, so see imap_mime_header_decode()
	        $this->attachments[] = array('filename' => $filename, 'data' => $data);  // this is a problem if two files have same name
	    } elseif ($p->type==0 && $data) { // TEXT
	        if (strtolower($p->subtype) == 'plain') {
	            $this->plainmsg .= trim($data) ."\n\n";
	        } else {
	            $this->htmlmsg .= $data ."<br /><br />";
			}
	        $this->charset = $params['charset']; // assume all parts are same charset
	    } elseif ($p->type==2 && $data) { // EMBEDDED MESSAGE
	        $this->plainmsg .= trim($data) ."\n\n";
	    }

	    // SUBPART RECURSION
	    if (isset($p->parts)) {
	        foreach ($p->parts as $partno0=>$p2) {
	            $this->getPart($connection,$uid,$p2,$partno.'.'.($partno0+1));  // 1.2, 1.2.1, etc.
			}
	    }
	}
	
}
